'use strict';

define(['angular', 'base64', 'angular-growl'], function (angular, base64) {
	var module = angular.module('mpjRest', ['angular-growl']);

	var GLOBAL_TIMEOUT = 15000;

	function RestError(code, msg, errType) {
		this.code = code;
		this.msg = msg;
		// app-error server-error network-error unknown-error
		this.errType = errType;
	}

	RestError.prototype.toString = function () {
		return this.msg;
	};

	RestError.prototype.setCode = function (code) {
		this.code = code;
	};

	RestError.prototype.setMsg = function (msg) {
		this.msg = msg;
	};

	RestError.prototype.setErrType = function (errType) {
		this.errType = errType;
	};

	module.factory("mpjRest", ['$rootScope', "$http", 'growl', function ($rootScope, $http, growl) {
		return {
			// config is optional，but can't be null
			get: get,
			post: post,
			put: put,
			delete: httpDelete,
			getUrl: getUrl
		};

		// $http，请参见angular文档 http://docs.angularjs.org/api/ng/service/$http
		// 关于config
		// method – {string} – HTTP method (e.g. 'GET', 'POST', etc)
		// url – {string} – Absolute or relative URL of the resource that is being requested.
		// params – {Object.<string|Object>} – Map of strings or objects which will be turned to ?key1=value1&key2=value2 after the url. If the value is not a string, it will be JSONified.
		// data – {string|Object} – Data to be sent as the request message data.
		// headers – {Object} – Map of strings or functions which return strings representing HTTP headers to send to the server. If the return value of a function is null, the header will not be sent.
		// xsrfHeaderName – {string} – Name of HTTP header to populate with the XSRF token.
		// xsrfCookieName – {string} – Name of cookie containing the XSRF token.
		// transformRequest – {function(data, headersGetter)|Array.<function(data, headersGetter)>} – transform function or an array of such functions. The transform function takes the http request body and headers and returns its transformed (typically serialized) version.
		// transformResponse – {function(data, headersGetter)|Array.<function(data, headersGetter)>} – transform function or an array of such functions. The transform function takes the http response body and headers and returns its transformed (typically deserialized) version.
		// cache – {boolean|Cache} – If true, a default $http cache will be used to cache the GET request, otherwise if a cache instance built with $cacheFactory, this cache will be used for caching.
		// timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.
		// withCredentials - {boolean} - whether to to set the withCredentials flag on the XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5 for more information.
		// responseType - {string} - see requestType.

		function get(url, config, cb, ownError) {
			if (typeof config === 'function') {
				ownError = cb;
				cb = config;
				config = {};
			}

			if (!config) {
				config = {};
			}

			addCommonConfig(config);

			return setCb($http.get(url, config), cb, ownError);
		}

		function post(url, data, config, cb, ownError) {
			if (typeof config === 'function') {
				ownError = cb;
				cb = config;
				config = {};
			}

			if (!config) {
				config = {};
			}

			addCommonConfig(config);

			return setCb($http.post(url, data, config), cb, ownError);
		}

		function put(url, data, config, cb, ownError) {
			if (typeof config === 'function') {
				ownError = cb;
				cb = config;
				config = {};
			}

			if (!config) {
				config = {};
			}

			addCommonConfig(config);

			return setCb($http.put(url, data, config), cb, ownError);
		}

		function httpDelete(url, config, cb, ownError) {
			if (typeof config === 'function') {
				ownError = cb;
				cb = config;
				config = {};
			}

			if (!config) {
				config = {};
			}

			addCommonConfig(config);

			return setCb($http.delete(url, config), cb, ownError);
		}

		function addCommonConfig(config) {
			config.headers && (config.headers['Cache-Control'] || (config.headers['Cache-Control'] = 'no-cache'));
			config.timeout || (config.timeout = GLOBAL_TIMEOUT);
		}

		function setCb(httpPromise, cb, ownError) {
			return httpPromise
				//success:0 data, status, headers, config
				.success(function (d, s, h, c) {
					cb && cb(null, d, s, h, c);
					return d;
				})
				//errorCb: errMsg, data, status, headers, config
				.error(function (d, s, h, c) {
					return defaultErrorCb(d, s, h, c, cb);
				});
		}

		function defaultErrorCb(data, status, headers, config, cb) {
			// angular将网络连接错误作为404处理
			// 正常我们的程序是不返回404的
			var errMsg = new RestError('', '', '');

			// TODO Pliman 需要在angular fix这个问题之后，把这里换掉
			if (status == 404) {
				return cb && cb('网络连接错误，请检查网络.', data, status, headers, config);
			}
			if (headers('mpj-exception') && !data) {
				data = JSON.parse(base64.decode(headers('mpj-exception')));
			}

			if (data && data.code) {
//				errMsg = data.code;
				errMsg.setCode(data.code);
				errMsg.setMsg(data.msg || data.code);
				errMsg.setErrType(data.type || 'server-error');
			} else {
				var errType = (status + '').charAt(0);
				errMsg.setCode(errType);
				switch (errType) {
					case '5':
//						errMsg = '获取服务器资源错误，请稍后重试或联系服务人员.';
						errMsg.setMsg('获取服务器资源错误，请稍后重试或联系服务人员.');
						errMsg.setErrType('server-error')
						break;
					case '4':
						// 'null'是resteasy框架报的错误
//						errMsg = (data == 'null') ? '发生未知错误，请联系服务人员.' : data;
						errMsg.setMsg((data == 'null') ? '发生未知错误，请联系服务人员.' : data);
						errMsg.setErrType('server-error')
						break;
					case '0':
//						errMsg = '网络连接超时，请刷新重试';
						errMsg.setMsg('网络连接超时，请刷新重试');
						errMsg.setErrType('network-error')
						break;
					default:
//						errMsg = '发生未知错误，请联系服务人员.';
						errMsg.setMsg('发生未知错误，请联系服务人员.');
						errMsg.setErrType('unknown-error')
				}
			}

			cb ? cb(errMsg, data, status, headers, config) : console.log(errMsg);
			return errMsg;
		}

//		function errorCb(data, status, headers, config, error) {
//			var errMsg = '';
//			if (data && data.code) {
//				errMsg = data.code;
//			} else {
//				errMsg = '获取服务器资源错误.'
//			}
//
//			error ? error(errMsg, data, status, headers, config) : console.log(errMsg);
//		}

		function getUrl(relativePath) {
			return $rootScope.system.apiPrefix + relativePath;
		}
	}]);
});
